﻿package {
	import flash.display.Graphics;
	import flash.display.Shape;
	import flash.geom.*;
	import flash.geom.ColorTransform;
	import flash.geom.Transform;
	
	public class MathUtilities {
		public static const ERROR_VAL:int = Number.NaN;
		
		
		public static function distance(x1:Number, y1:Number, x2:Number, y2:Number):Number
		{
			return (Math.sqrt(Math.pow(x1-x2,2) + Math.pow(y1-y2,2)));
		}
		public static function distanceP(p1:Point, p2:Point):Number
		{
			return distance(p1.x, p1.y, p2.x, p2.y);
		}
		/** a, b, and c (optional) are distances, C is an angle in radians, either c or C is left blank */
		public static function lawOfCosines(a:Number, b:Number, c:Number = 0, C:Number = 0):Number
		{
			// Use law of cosines to retrieve angle (in radians)
			if (C != 0 && c == 0)
			{
				return Math.sqrt(a*a + b*b - 2*a*b * Math.cos(C));
			} else if (C == 0 && c != 0)
			{
				return  Math.acos((c*c - a*a - b*b)/(-2*a*b));
			} else
			{
				return Number.NaN;				
			}
			
		}
		/** in radians:  a/sin(A) = b/sin(B) ==>  b = a/sinA*sinB || sinB = b*sinA/a  */ 
		public static function lawOfSines(a:Number, A:Number, b:Number = 0, B:Number = 0):Number
		{
			if (b == 0 && B != 0)
			{
				return (Math.sin(B)/Math.sin(A)*a);	
			} else if (b != 0 && B == 0)
			{
				return (Math.asin(b*Math.sin(A)/a));	
			} else 
			{
				trace("inappropriate parameters to law of sines");
				return Number.NaN;
			}
		}
		public static function invSlope(x1:Number, y1:Number, x2:Number, y2:Number):Number{
			return invSlopePoint(new Point(x1,y1), new Point(x2, y2));
		}
		public static function invSlopePoint(point1:Point, point2:Point):Number{
			return (-1/((point2.y-point1.y)/(point2.x-point1.x)));
		}
		public static function slope(x1:Number, y1:Number, x2:Number, y2:Number):Number{
			return slopePoint(new Point(x1,y1), new Point(x2, y2));
		}
		public static function slopePoint(point1:Point, point2:Point):Number{
			return ((point2.y-point1.y)/(point2.x-point1.x));
		}
		public static function intercept(x1:Number, y1:Number, x2:Number, y2:Number):Number{
			var point1:Point = new Point(x1,y1);
			var point2:Point = new Point(y1,y2);
			var slope:Number = slopePoint(point1, point2);
			return (interceptFromSlopePoint(point1, slope));
		}
		public static function interceptFromSlope(x1:Number, y1:Number, slope:Number):Number{
			// y = mx + b   // b = y - mx
			var point1:Point = new Point(x1,y1);
			return (point1.y - slope*point1.x);
		}
		public static function interceptPoint(point1:Point, point2:Point):Number{
			var slope:Number = slopePoint(point1, point2);
			return (interceptFromSlopePoint(point1, slope));
		}
		public static function interceptFromSlopePoint(point1:Point, slope:Number):Number{
			// y = mx + b   // b = y - mx
			return (point1.y - slope*point1.x);
		}
		public static function xinterceptFromSlope(x1:Number, y1:Number, slope:Number):Number {
			// y = mx + b // 0 = mx2 + b // -b = mx2 // x2 = -b/m
			var b:Number = interceptFromSlope(x1, y1, slope);
			return (-b/slope);
		}
		public static function intersection(slope1:Number, intercept1:Number, slope2:Number, intercept2:Number):Point{
			// y = m1x+b1
			// y = m2x+b2
			// m1x+b1 = m2x+b2
			// x(m1-m2) = (b2-b1) =>  x = (b2-b1)/(m1-m2)
			if (slope1 == Number.POSITIVE_INFINITY)	{slope1 = Number.MAX_VALUE;} 
			else if (slope1 == Number.NEGATIVE_INFINITY){slope1 = Number.MIN_VALUE;} 
			if (slope2 == Number.POSITIVE_INFINITY)	{slope2 = Number.MAX_VALUE;} 
			else if (slope2 == Number.NEGATIVE_INFINITY){slope2 = Number.MIN_VALUE;} 
			if (intercept1 == Number.POSITIVE_INFINITY)	{intercept1 = Number.MAX_VALUE;} 
			else if (intercept1 == Number.NEGATIVE_INFINITY){intercept1 = Number.MIN_VALUE;} 
			if (intercept2 == Number.POSITIVE_INFINITY)	{intercept2 = Number.MAX_VALUE;} 
			else if (intercept2 == Number.NEGATIVE_INFINITY){intercept2 = Number.MIN_VALUE;} 
			var x:Number = (intercept2-intercept1)/(slope1-slope2);
			// y = m1x+b1
			var y:Number = slope1*x + intercept1;
			return new Point(x,y);
		}
		/** with points (1 and 2) on L1 and points (1,2) on L2, find intersection */ 		
		public static function intersectionLines(point1L1:Point, point2L1:Point, point1L2:Point, point2L2:Point):Point{
			var s1:Number, s2:Number, i1:Number, i2:Number;
			if (point1L1.x != point2L1.x && point1L2.x != point2L2.x)
			{
				s1 = slopePoint(point1L1, point2L1);
				i1 = interceptPoint(point1L1, point2L1);
				s2 = slopePoint(point1L2, point2L2);
				i2 = interceptPoint(point1L2, point2L2);
				return(intersection(s1, i1, s2, i2));
			} else if (point1L1.x == point2L1.x && point1L2.x != point2L2.x)
			{ // Line 1 is vertical, use x to find intersection
				s2 = slopePoint(point1L2, point2L2);
				i2 = interceptPoint(point1L2, point2L2);
				return (new Point(point1L1.x, s2*point1L1.x + i2));
			} else if (point1L1.x != point2L1.x && point1L2.x == point2L2.x)
			{ // Line 1 is vertical, use x to find intersection
				s1 = slopePoint(point1L1, point2L1);
				i1 = interceptPoint(point1L1, point2L1);
				return (new Point(point1L2.x, s1*point1L2.x + i1));
			}
			else
			{
				return (null);
			}
		}
		/** with points (1 and 2) on segment 1 and points (1,2) on segment 2, find intersection */ 		
		public static function intersectionSegments(point1S1:Point, point2S1:Point, point1S2:Point, point2S2:Point, includeEndpoints:Boolean=true):Point
		{
			var s1:Number, s2:Number, i1:Number, i2:Number;
			var i:Point;
			if (point1S1==null || point2S1==null || point1S2==null || point2S2==null) return null;
			if (point1S1.x != point2S1.x && point1S2.x != point2S2.x)
			{
				s1 = slopePoint(point1S1, point2S1);
				i1 = interceptPoint(point1S1, point2S1);
				s2 = slopePoint(point1S2, point2S2);
				i2 = interceptPoint(point1S2, point2S2);
				i = intersection(s1, i1, s2, i2);
				//trace (s1,i1, s2, i2,  i);
				
				if (i != null)
				{
					if (includeEndpoints)
					{
						if (i.x >= Math.min(point1S1.x, point2S1.x) && i.x <= Math.max(point1S1.x, point2S1.x) && i.y >= Math.min(point1S1.y, point2S1.y) && i.y <= Math.max(point1S1.y, point2S1.y) && i.x >= Math.min(point1S2.x, point2S2.x) && i.x <= Math.max(point1S2.x, point2S2.x) && i.y >= Math.min(point1S2.y, point2S2.y) && i.y <= Math.max(point1S2.y, point2S2.y))
						{
							//trace(i, "compare to (", Math.min(point1S1.x, point2S1.x), Math.max(point1S1.x, point2S1.x),",", Math.min(point1S1.y, point2S1.y), Math.max(point1S1.y, point2S1.y),") (", Math.min(point1S2.x, point2S2.x), Math.max(point1S2.x, point2S2.x),",", Math.min(point1S2.y, point2S2.y), Math.max(point1S2.y, point2S2.y), ")");
							return i;
						} else
						{
							return null;
						}
					} else
					{
						if (i.x > Math.min(point1S1.x, point2S1.x) && i.x < Math.max(point1S1.x, point2S1.x) && i.y >= Math.min(point1S1.y, point2S1.y) && i.y <= Math.max(point1S1.y, point2S1.y) && i.x > Math.min(point1S2.x, point2S2.x) && i.x < Math.max(point1S2.x, point2S2.x) && i.y >= Math.min(point1S2.y, point2S2.y) && i.y <= Math.max(point1S2.y, point2S2.y))
						{
							return i;
						} else
						{
							return null;
						}
					}
				} else
				{
					return null;
				}
			} else if (point1S1.x == point2S1.x && point1S2.x != point2S2.x)
			{ // Sine 1 is vertical, use x to find intersection
				s2 = slopePoint(point1S2, point2S2);
				i2 = interceptPoint(point1S2, point2S2);
				i = new Point(point1S1.x, s2*point1S1.x + i2);
				if (i != null)
				{
					if (includeEndpoints)
					{
						if (i.y >= Math.min(point1S1.y, point2S1.y) && i.y <= Math.max(point1S1.y, point2S1.y) && i.x >= Math.min(point1S2.x, point2S2.x) && i.x <= Math.max(point1S2.x, point2S2.x) && i.y >= Math.min(point1S2.y, point2S2.y) && i.y <= Math.max(point1S2.y, point2S2.y))
						{
							//trace(i, "compare to (", Math.min(point1S1.x, point2S1.x), Math.max(point1S1.x, point2S1.x),",", Math.min(point1S1.y, point2S1.y), Math.max(point1S1.y, point2S1.y),") (", Math.min(point1S2.x, point2S2.x), Math.max(point1S2.x, point2S2.x),",", Math.min(point1S2.y, point2S2.y), Math.max(point1S2.y, point2S2.y), ")");
							return i;
						} else
						{
							return null;
						}
					} else
					{
						if (i.y >= Math.min(point1S1.y, point2S1.y) && i.y <= Math.max(point1S1.y, point2S1.y) && i.x > Math.min(point1S2.x, point2S2.x) && i.x < Math.max(point1S2.x, point2S2.x) && i.y >= Math.min(point1S2.y, point2S2.y) && i.y <= Math.max(point1S2.y, point2S2.y))
						{
							return i;
						} else
						{
							return null;
						}
					}
				} else
				{
					return null;
				}
			} else if (point1S1.x != point2S1.x && point1S2.x == point2S2.x)
			{ // Sine 1 is vertical, use x to find intersection
				s1 = slopePoint(point1S1, point2S1);
				i1 = interceptPoint(point1S1, point2S1);
				i = new Point(point1S2.x, s1*point1S2.x + i1);
				if (i != null)
				{
					if (includeEndpoints)
					{
						if (i.x >= Math.min(point1S1.x, point2S1.x) && i.x <= Math.max(point1S1.x, point2S1.x) && i.y >= Math.min(point1S1.y, point2S1.y) && i.y <= Math.max(point1S1.y, point2S1.y) && i.y >= Math.min(point1S2.y, point2S2.y) && i.y <= Math.max(point1S2.y, point2S2.y))
						{
							//trace(i, "compare to (", Math.min(point1S1.x, point2S1.x), Math.max(point1S1.x, point2S1.x),",", Math.min(point1S1.y, point2S1.y), Math.max(point1S1.y, point2S1.y),") (", Math.min(point1S2.x, point2S2.x), Math.max(point1S2.x, point2S2.x),",", Math.min(point1S2.y, point2S2.y), Math.max(point1S2.y, point2S2.y), ")");
							return i;
						} else
						{
							return null;
						}
					} else
					{
					if (i.x > Math.min(point1S1.x, point2S1.x) && i.x < Math.max(point1S1.x, point2S1.x) && i.y >= Math.min(point1S1.y, point2S1.y) && i.y <= Math.max(point1S1.y, point2S1.y) && i.y >= Math.min(point1S2.y, point2S2.y) && i.y <= Math.max(point1S2.y, point2S2.y))
						{
							return i;
						} else
						{
							return null;
						}
					}
				} else
				{
					return null;
				}
			} // parallel vertical segments
			else
			{
				return (null);
			}			
		}
		/** with pointPerp perpendicular to line from point1 to point2, find intersection */
		public static function intersectionPerpPoints(pointPerp:Point, point1:Point, point2:Point):Point
		{ 
			if (point1.x == point2.x)
			{
				return new Point(point1.x, pointPerp.y);
			} else if (point1.y == point2.y)
			{
				return new Point(pointPerp.x, point1.y);
			} else 
			{
				var s1:Number = slopePoint(point1, point2);
				var i1:Number = interceptPoint(point1, point2);
				// inverse slope
				var s2:Number;
				if (s1 == Number.POSITIVE_INFINITY || s1 == Number.NEGATIVE_INFINITY) 
				{
					s2 = 0;
				} else if (s1 == 0)
				{
					s2 = Number.MAX_VALUE;
				} else
				{
					s2 = -1/s1;
				}
				var i2:Number = interceptFromSlope(pointPerp.x, pointPerp.y, s2);
				return(intersection(s1, i1, s2, i2));
			}
		}
		public static function midpoint(x1:Number, y1:Number, x2:Number, y2:Number):Point{
			return midpointPoint(new Point(x1,y1), new Point(x2, y2));
		}
		public static function midpointPoint(point1:Point, point2:Point):Point{
			return (new Point((point1.x+point2.x)/2, (point1.y+point2.y)/2));
		}
		/** Return two points along the same line as (x1,y1); (x2,y2), but out of the bounds given.  
		    This is used to construct a line that stretches across a whole screen */
		public static function getPointsOutOfBoundsOnLine(x1:Number, y1:Number, x2:Number, y2:Number, WIDTH:Number, HEIGHT:Number):Array{
			// Calculate angle between points
			var angle:Number = calcAngleXY(x1,y1,x2,y2);
			var minDistance:Number = Math.sqrt(WIDTH*WIDTH+HEIGHT*HEIGHT);
			// calculate from the first point
			var point1:Point = new Point(x1+minDistance*Math.sin(angle),y1-minDistance*Math.cos(angle));
			var point2:Point = new Point(x1-minDistance*Math.sin(angle),y1+minDistance*Math.cos(angle));
			return new Array(point1,point2);
		}
		public static function getPointsOutOfBoundsOnLineRot(x1:Number, y1:Number, rot:Number, WIDTH:Number, HEIGHT:Number):Array{
			// get some arbitrary point on the same line
			
			var x2:Number = x1+100*Math.sin(degToRad(rot));
			var y2:Number = y1+100*-Math.cos(degToRad(rot));
					
			return getPointsOutOfBoundsOnLine(x1,y1,x2,y2, WIDTH, HEIGHT);		
		}
		/** Return two points along the same line as (x1,y1); (x2,y2), but out of the bounds given.  
		    This is used to construct a line that stretches across a whole screen */
		public static function getPointsOutOfBoundsOnLinePerpendicular(x1:Number, y1:Number, x2:Number, y2:Number, WIDTH:Number, HEIGHT:Number):Array{
			// Calculate angle between points
			var angle:Number = calcAngleXY(x1,y1,x2,y2)+Math.PI/2;
			var minDistance:Number = Math.sqrt(WIDTH*WIDTH+HEIGHT*HEIGHT);
			// calculate from the first point
			var point1:Point = new Point(x2+minDistance*Math.sin(angle),y2-minDistance*Math.cos(angle));
			var point2:Point = new Point(x2-minDistance*Math.sin(angle),y2+minDistance*Math.cos(angle));
			return new Array(point1,point2);
		}
		/** Rotate all points by the given degrees, around reference point */
		public static function calcRotatedPoints(degrees:Number, points:Array, rpoint:Point=null):Array
		{
			if (points == null) return null;
			if (rpoint==null)rpoint = MathUtilities.centroid(points);	
			// centered points
			/*
			var cpoints:Array = new Array();
			for each (var p:Point in points)
			{
				cpoints.push(new Point(p.x-rpoint.x, p.y-rpoint.y));
			}
			var rpoints:Array = new Array();
			*/
			var angle:Number = MathUtilities.degToRad(degrees);
			for each (var p:Point in points)
			{
				var dist:Number = MathUtilities.distanceP(p, rpoint);
				var old_orientation:Number = MathUtilities.calcAngle(p, rpoint);
				var new_orientation:Number = old_orientation+angle;
				p.x = rpoint.x + dist*Math.sin(new_orientation);
				p.y = rpoint.y - dist*Math.cos(new_orientation);
				
			}
			/*
			for each (p in cpoints)
			{
				rpoints.push(new Point(p.x*Math.cos(angle)-p.y*Math.sin(angle),p.x*Math.sin(angle)+p.y*Math.cos(angle)));
			}
			
			// uncenter points
			points = new Array();
			for each (p in rpoints)
			{
				points.push(new Point(p.x+rpoint.x, p.y+rpoint.y));
			}
			*/
			return points;
		}
		public static function calcPointFromZeroWithDegrees(deg:Number, dist:Number):Point{
			var point:Point = new Point();
			point.x = Math.sin(degToRad(deg))*dist;
			point.y = Math.cos(degToRad(deg))*-dist;
			return point;
		}
		/** Calculates a point dist from rp on the same line as op.  positive is distal, negative is between */
		public static function calcPointDistFromPoints(dist:Number, rp:Point, op:Point):Point
		{ 
			var a:Number = calcAngle(rp, op);
			return new Point(rp.x+Math.sin(a)*dist, rp.y-Math.cos(a)*dist);
		}
		/**  If the angle of p2 is greater than p1 1 is returned, if the angle of p1 is greater than p2 -1 is returned, if they are the same or 180
		 degrees then 0 is returned */
		public static function calcAcuteDirectionBetween(p1:Point, p2:Point, centerP:Point):Number 
		{
			var angle1:Number = calcAngle(p1,centerP);
			var angle2:Number = calcAngle(p2,centerP);
			return calcAcuteDirection(angle1,angle2);
		}
		public static function calcAcuteDirectionDeg(deg1:Number, deg2:Number):Number
		{
			return calcAcuteDirection(degToRad(deg1), degToRad(deg2));
		}
		public static function calcAcuteDirection(angle1:Number, angle2:Number):Number
		{
			var diff:Number = angle1 - angle2;
			if (Math.abs(diff) > 0 && Math.abs(diff) < Math.PI){
				return diff/Math.abs(diff);
			} else if (Math.abs(diff) > Math.PI ){
				return -1*diff/Math.abs(diff);
			} else {
				return 0;
			}
		}
		
		public static function calcAcuteRotationBetween(p1:Point, p2:Point, centerP:Point):Number{
			var rot1:Number = degToRad(calcRotation(p1,centerP));
			var rot2:Number = degToRad(calcRotation(p2,centerP));
			return radToDeg(calcAcuteAngle(rot1, rot2));			
		}
		/**This method finds the angle between the two points in the center.  Should use this function because
		it adjusts if the angle crosses the zero */
		public static function calcAcuteAngleBetween(p1:Point, p2:Point, centerP:Point):Number{
			var rot1:Number = calcAngle(p1,centerP);
			var rot2:Number = calcAngle(p2,centerP);
			return calcAcuteAngle(rot1, rot2);			
		}
		public static function calcAcuteRotationAbs(rot1:Number, rot2:Number):Number
		{
			if (Math.abs(rot1) == 180) rot1 = 0;
			if (Math.abs(rot2) == 180) rot2 = 0;
			if (Math.abs(rot1-rot2) <= 90){
				return Math.abs(rot1-rot2);
			} else {
				if (rot1 > rot2){
					rot2 += 180;
				} else {
					rot1 += 180;
				}			
				return Math.abs(rot1-rot2);
			}
		}
		public static function calcAcuteRotation(rot1:Number, rot2:Number):Number
		{
			if (Math.abs(rot1-rot2) <= 180){
				return Math.abs(rot1-rot2);
			} else {
				if (rot1 > rot2){
					rot2 += 360;
				} else {
					rot1 += 360;
				}			
				return Math.abs(rot1-rot2);
			}
		}
		public static function calcAcuteAngleAbs(angle1:Number, angle2:Number):Number
		{
			if (Math.abs(angle1-angle2) <= Math.PI/2){
				return Math.abs(angle1-angle2);
			} else {
				if (angle1 > angle2){
					angle2 += Math.PI;
				} else {
					angle1 += Math.PI;
				}			
				return Math.abs(angle1-angle2);
			}
		}
		public static function calcAcuteAngle(angle1:Number, angle2:Number):Number
		{
			if (Math.abs(angle1-angle2) <= Math.PI){
				return Math.abs(angle1-angle2);
			} else {
				if (angle1 > angle2){
					angle2 += Math.PI * 2;
				} else {
					angle1 += Math.PI * 2;
				}			
				return Math.abs(angle1-angle2);
			}
		}
		/*** not fully checked out */
		// calculate rotation at this goalpoint (segment) from the new angle
				// oRot, nAngle, nRot
				/// 0, 45, 135::: 0, 135, 45::: 45, 90, 135::: 45, 45, 180::: 45, 135, 90::: 135, 45,-90(270)::: 135, 135, 180::: 135, 90, -135(225),1
				/// 0, -45, -135::: 0, -135, -45::: 45, -45, -90::: 45, -135, 0::: 135, -135, 90::: 135,-90, 45
				/// -45, 45, 90::: -45, 135, 0::: -135, 45, 0::: -135, 135, -90
				/// -45, -45, -180::: -45, -135, -90::: 
				/// dir*((180 - nAngle) + oRot
				
		public static function calcRotationFromAngle(angle:Number, oldRotation:Number, direction:int):Number
		{
			return (direction * (180 - angle) + oldRotation) ;
		}
		public static function calcAngleBisector(angle1:Number, angle2:Number):Number
		{
			var bisector:Number = (angle1 + angle2)/2;
			if (Math.abs(angle1 - angle2) > Math.PI){
				bisector = Math.PI + bisector;
			}
			return bisector;
		}
		/** Returns rotation from 0-180 in degrees*/
		public static function calcRotationAbs(distP:Point, centerP:Point):Number{
			return (radToDeg(calcAngle(distP,centerP))+180)%180;
		}
		/** Returns rotation from 0-180 in degrees*/
		public static function calcAngleAbs(distP:Point, centerP:Point):Number{
			return (calcAngle(distP,centerP)+Math.PI)%Math.PI;
		}
		/** Returns rotation in degrees*/
		public static function calcRotation(distP:Point, centerP:Point):Number{
			return radToDeg(calcAngle(distP,centerP));
		}
		/** Returns rotation in radians */
		public static function calcAngle(distP:Point, centerP:Point):Number{
			var distx:Number = distP.x - centerP.x;
			var disty:Number = distP.y - centerP.y;
			if (distP.x == centerP.x && distP.y == centerP.y){
				return 0;
			} else {	
				var rot:Number = Math.atan(Math.abs(distx)/Math.abs(disty));
				if (distx < 0 && disty <= 0){ //Top Left
					rot =  -1* rot; 	
				} else if (distx > 0 && disty >= 0){ //Bottom Right
					rot = Math.PI - rot;
				} else if (distx <= 0 && disty > 0){ //Bottom Left
					rot = rot + -1*Math.PI;
				} else { // Top Right
	
				}
				return rot;
			}
		}
		/* Returns rotation in degrees */
		public static function calcRotationXY(dx:Number, dy:Number, cx:Number, cy:Number):Number{
			return radToDeg(calcAngleXY(dx,dy,cx,cy));
		}
		
		/* Returns rotation in radians */
		public static function calcAngleXY(dx:Number, dy:Number, cx:Number, cy:Number):Number{
			var distx:Number = dx - cx;
			var disty:Number = dy - cy;
			
			if (dx == cx && dy == cy){
				return 0;
			} else {	
				var rot:Number = Math.atan(Math.abs(distx)/Math.abs(disty));
				if (distx < 0 && disty <= 0){ //Top Left
					rot = -1 * rot; 	
				} else if (distx > 0 && disty >= 0){ //Bottom Right
					rot = Math.PI - rot;
				} else if (distx <= 0 && disty > 0){ //Bottom Left
					rot = rot + -1*Math.PI;
				} else { // Top Right
	
				}
				return rot;
			}
		}
		/* Converts rotation (-180:180) to grade (-100:100) */
		public static function degToGrade(deg:Number):int{
			if (deg > 180){
				deg = -1*(360-deg);
			} else if (deg < -180){
				deg = (360+deg);
			}
			// negative degrees are no matter to grade, convert to positive
			if (deg < 0){
				deg += 180;
			}
			var grade:Number = -1*(deg - 90)/90 * 100;
			if (grade == -100) grade = 100;
			return (grade);
		}
		/* Converts grade to a positive rotation */
		public static function gradeToPosDeg(grade:Number):Number
		{
			if (grade >= 0){
				return ( (1-grade/100)*90);
			} else {
				return ( (-grade/100)*90+90);
			}
		}
		/* converts rotation to slope */
		public static function degToSlope(deg:Number):Number{
			if (deg == 0){ deg = .0001;} // slight deviation so what we don't get an error
			else if(deg == 180){ deg = 179.9999;}
			else if(deg == -180){ deg = -179.9999;}
			var grade:Number = degToGrade(deg);
			
			return Math.tan(-grade/100*Math.PI/2); 
		}
		public static function degToRad(deg:Number):Number{
			if (deg > 180){
				deg = -1*(360-deg);
			} else if (deg < -180){
				deg = (360+deg);
			}
			var rad:Number = deg * Math.PI/180;
			return rad;
		}
		public static function radToDeg(rad:Number):Number{
			var deg:Number = rad * 180 / Math.PI;
			if (deg > 180){
				rad = -1*(360-deg);
			} else if (deg < -180){
				rad = (360+deg);
			}
			return deg;
		}
		
		public static function drawArc(g:Graphics, linesize:Number, color:uint, centerX:Number, centerY:Number, startAngle:Number, endAngle:Number, radius:Number, direction:Number=0):int             
		/* 
			centerX  -- the center X coordinate of the circle the arc is located on
			centerY  -- the center Y coordinate of the circle the arc is located on
			startAngle  -- the starting angle to draw the arc from
			endAngle    -- the ending angle for the arc
			radius    -- the radius of the circle the arc is located on
			direction   -- toggle for going clockwise/counter-clockwise
		*/
		{
			var difference:Number; 
			g.lineStyle(linesize,color);
			
			if (direction==0)
			{
				if (calcAcuteDirection(startAngle,endAngle)==1)
				{
					direction = 1;
					var _angle:Number = endAngle;
					endAngle = startAngle;
					startAngle = _angle;
				} else
				{
					direction = 1;
				}
				
			}
			
			if (endAngle > startAngle){
				difference = Math.abs(endAngle - startAngle);
				// How "far" around we actually have to draw 
			} else { //the user crosses over 360 degrees into the first quadrant
				difference = Math.abs( (endAngle + Math.PI*2) - startAngle); //go past one full rotation of the circle
			}
			
			var divisions:Number = Math.floor(difference / (Math.PI / 4))+1;
			// The number of arcs we are going to use to simulate our simulated arc
			
			var span:Number    = direction * difference / (2 * divisions);
			var controlRadius:Number    = radius / Math.cos(span);
			//trace((startAngle*180/Math.PI) + " , " + (endAngle*180/Math.PI));
			//trace(Math.sin(startAngle)*radius+" , "+centerY + Math.cos(startAngle)*radius);
			g.moveTo(centerX + (Math.sin(startAngle)*radius), centerY - Math.cos(startAngle)*radius);
			var controlPoint:Point;
			var anchorPoint:Point;
			for(var i:Number=0; i<divisions; ++i)
			{
				endAngle    = startAngle + span;
				startAngle  = endAngle + span;
				
				controlPoint = new Point(centerX+Math.sin(endAngle)*controlRadius, centerY-Math.cos(endAngle)*controlRadius);
				anchorPoint = new Point(centerX+Math.sin(startAngle)*radius, centerY-Math.cos(startAngle)*radius);
				g.curveTo(
					controlPoint.x,
					controlPoint.y,
					anchorPoint.x,
					anchorPoint.y
				);
			}
			return divisions;
		}
		/** Finds the width of a bounding box*/
		public static function findBoundingWidth(points:Array):Number
		{
			var minx:Number=Number.MAX_VALUE;
			var maxx:Number=Number.MIN_VALUE;
			for each (var p:Point in points)
			{
				if (p.x < minx) minx = p.x;
				if (p.x > maxx) maxx = p.x;	
			}
			return (maxx - minx);
		}
		/** Finds the width of a bounding box*/
		public static function findBoundingHeight(points:Array):Number
		{
			var miny:Number=Number.MAX_VALUE;
			var maxy:Number=Number.MIN_VALUE;
			for each (var p:Point in points)
			{
				if (p.y < miny) miny = p.y;
				if (p.y > maxy) maxy = p.y;	
			}
			return (maxy - miny);
		}
		/** Finds the area of the points */
		public static function area(points:Array):Number
		{
			return (Math.abs(signedarea(points)));
		}
		public static function signedarea(points:Array):Number
		{
			var _area:Number = 0;
			// get min x, min y less than 0
			var minx:Number=0, miny:Number=0;
			for each (var p:Point in points)
			{
				if (p.x < minx) minx = p.x;
				if (p.y < miny) miny = p.y;
			}
			// adjust to mins
			for each (p in points)
			{
				p.x -= minx;
				p.y -= miny;
			}
			for (var i:int=0; i < points.length; i++)
			{
				var p1:Point = points[i];
				var p2:Point = points[(i+1)%points.length];
				_area += (p1.x * p2.y) - (p2.x*p1.y);
			}
			return _area/2;
		}
		/** Returns the centroid (center of mass) of the given points */
		public static function centroid(points:Array):Point
		{
			var xsum:Number = 0;
			var ysum:Number = 0;
			var area:Number = MathUtilities.signedarea(points);
			for (var i:int=0; i < points.length; i++)
			{
				var p1:Point = points[i];
				var p2:Point = points[(i+1)%points.length];
				xsum += (p1.x + p2.x) * (p1.x*p2.y - p2.x*p1.y);
				ysum += (p1.y + p2.y) * (p1.x*p2.y - p2.x*p1.y);
			}
			return new Point(xsum/(6*area), ysum/(6*area));
		}
		
		/** This function uses the two peasants method to draw a relatively normal looking polygon (we hope)
		 * runs in nlogn time */
		public static function orderPointsPolygon(points:Array, TESTING:Boolean = false):Array
		{
			var npoints:Array = new Array();
			var botpoints:Array = new Array();
			var toppoints:Array = new Array();
			// find the extreme (x) points
			var xmin:int = int.MAX_VALUE;
			var xmax:int = int.MIN_VALUE;
			var minp:Point;
			var maxp:Point;
			for each (var p:Point in points)
			{
				if (p.x < xmin)
				{
					minp = p;
					xmin = p.x;
				}
				if (p.x > xmax)
				{
					maxp = p;
					xmax = p.x;
				}
				if(TESTING)trace("point", p.x, p.y);
			}
			if (maxp == minp)return null;
			if(TESTING)trace("min point", minp.x, minp.y);
			if(TESTING)trace("max point", maxp.x, maxp.y);
			// sort points into upper and lower half
			for each (p in points)
			{
				if (p != maxp && p != minp)
				{
					switch (MathUtilities.calcAcuteDirectionBetween(minp, maxp, p))
					{
						case 1:
						case 0:
							botpoints.push(p);
							break;
						case -1:
							toppoints.push(p);
							break;
						default:
							return null;
					}
				}
			}
			
			// go through each half and connect points to closest point
			var closep:Point, thisp:Point, lastp:Point = minp;
			var closepI:int=-1;
			var minDist:Number;
			// the min point is first on final array
			npoints.push(minp);
			if(TESTING)trace("min point", minp.x, minp.y);
			if(TESTING){for each (p in botpoints){trace("bottom point", p.x, p.y)}}
			while (botpoints.length > 0)
			{
				minDist = Number.MAX_VALUE;
				for (var j:int=0; j < botpoints.length; j++)
				{
					thisp = botpoints[j];
					var d:Number = MathUtilities.distance(thisp.x, thisp.y, lastp.x, lastp.y);
					if (d < minDist)
					{
						closep = thisp;
						closepI = j;
						minDist = d;						
					}
				}
				if(TESTING)trace("closest bottom", closep.x, closep.y);
				lastp = closep; // make closest the last
				botpoints.splice(closepI, 1);
				npoints.push(closep);
			}
			// go through top half
			// the max point is next on final array
			lastp = maxp;
			npoints.push(maxp);
			if(TESTING)trace("max point", maxp.x, maxp.y);
			if(TESTING){for each (p in toppoints){trace("top point", p.x, p.y)}}
			while (toppoints.length > 0)
			{
				minDist = Number.MAX_VALUE;
				for (j=0; j < toppoints.length; j++)
				{
					thisp = toppoints[j];
					d = MathUtilities.distance(thisp.x, thisp.y, lastp.x, lastp.y);
					if (d < minDist)
					{
						closep = thisp;
						closepI = j;
						minDist = d;						
					}
				}
				if(TESTING)trace("closest top", closep.x, closep.y);
				lastp = closep; // make closest the last
				toppoints.splice(closepI, 1);
				npoints.push(closep);
			}
			return npoints;
		}
		
		/** This function uses the two peasants method to draw a relatively normal looking polygon (we hope)
		 * runs in nlogn time */
		public static function orderDrawPointsPolygon(points:Array, TESTING:Boolean = false):Array
		{
			var npoints:Array = new Array();
			var botpoints:Array = new Array();
			var toppoints:Array = new Array();
			// find the extreme (x) points
			var xmin:int = int.MAX_VALUE;
			var xmax:int = int.MIN_VALUE;
			var minp:DrawPoint;
			var maxp:DrawPoint;
			for each (var p:DrawPoint in points)
			{
				if (p.x < xmin)
				{
					minp = p;
					xmin = p.x;
				}
				if (p.x > xmax)
				{
					maxp = p;
					xmax = p.x;
				}
				if(TESTING)trace("point", p.x, p.y);
			}
			if (maxp == minp)return null;
			if(TESTING)trace("min point", minp.x, minp.y);
			if(TESTING)trace("max point", maxp.x, maxp.y);
			// sort points into upper and lower half
			for each (p in points)
			{
				if (p != maxp && p != minp)
				{
					switch (MathUtilities.calcAcuteDirectionBetween(new Point(minp.x, minp.y),new Point(maxp.x, maxp.y), new Point(p.x, p.y)))
					{
						case 1:
							botpoints.push(p);
							break;
						case -1:
							toppoints.push(p);
							break;
						default:
							return null;
					}
				}
			}
			
			// go through each half and connect points to closest point
			var closep:DrawPoint, thisp:DrawPoint, lastp:DrawPoint = minp;
			var closepI:int=-1;
			var minDist:Number;
			// the min point is first on final array
			npoints.push(minp);
			if(TESTING)trace("min point", minp.x, minp.y);
			if(TESTING){for each (p in botpoints){trace("bottom point", p.x, p.y)}}
			while (botpoints.length > 0)
			{
				minDist = Number.MAX_VALUE;
				for (var j:int=0; j < botpoints.length; j++)
				{
					thisp = botpoints[j];
					var d:Number = MathUtilities.distance(thisp.x, thisp.y, lastp.x, lastp.y);
					if (d < minDist)
					{
						closep = thisp;
						closepI = j;
						minDist = d;						
					}
				}
				if(TESTING)trace("closest bottom", closep.x, closep.y);
				lastp = closep; // make closest the last
				botpoints.splice(closepI, 1);
				npoints.push(closep);
			}
			// go through top half
			// the max point is next on final array
			lastp = maxp;
			npoints.push(maxp);
			if(TESTING)trace("max point", maxp.x, maxp.y);
			if(TESTING){for each (p in toppoints){trace("top point", p.x, p.y)}}
			while (toppoints.length > 0)
			{
				minDist = Number.MAX_VALUE;
				for (j=0; j < toppoints.length; j++)
				{
					thisp = toppoints[j];
					d = MathUtilities.distance(thisp.x, thisp.y, lastp.x, lastp.y);
					if (d < minDist)
					{
						closep = thisp;
						closepI = j;
						minDist = d;						
					}
				}
				if(TESTING)trace("closest top", closep.x, closep.y);
				lastp = closep; // make closest the last
				toppoints.splice(closepI, 1);
				npoints.push(closep);
			}
			return npoints;
		}
		/** If shape is convex return true, if shapes is concave returns false */
		public static function checkConvexity(points:Array, includeStraight:Boolean=false):Boolean
		{
			if (points == null || points.length < 3) return false;
			
			var p1:Point, p2:Point, pc:Point;
			// check angles
			var concavities:uint = 0;
			// get direction of concavity
			var dir:int;
			var i:int = 0;
			p1 = points[(i-1+points.length)%points.length]; 
			pc = points[i];
			p2 = points[(i+1)%points.length];
			// check for convexity, if necessary
			dir = MathUtilities.calcAcuteDirectionBetween(p1, p2, pc);
			
			for (i = 1; i < points.length; i++)
			{
				p1 = points[(i-1+points.length)%points.length]; 
				pc = points[i];
				p2 = points[(i+1)%points.length];
				// check for convexity, if necessary
				if (MathUtilities.calcAcuteDirectionBetween(p1, p2, pc) != dir) concavities++;
			}
			if (concavities > 0) 
			{
				return false;
			} else 
			{
				return true;
			}
		}
		
		/** This function check a set of points by geometric criterion, as defined by the type 
		 * Returns an array that contains a boolean and an error message (if false)
		 * */
		public static function checkShapeType(points:Array, type:String, parallel_threshold_degrees:Number = 1, length_threshold_pixels:Number=5, angle_threshold_degrees:Number=1, right_threshold_degrees:Number=1):Array
		{
			var p:Point;
			var i:int;
			var _type:String = type.split("_").join(" ");
			if (_type.substr(0,1) == "a" || _type.substr(0,1) == "e" || _type.substr(0,1) == "i" || _type.substr(0,1) == "o" || _type.substr(0,1) == "u")
			{
				_type = "An " + _type;
			} else
			{
				_type = "A " + _type;
			}
			var npoints:uint;
			var typeSplit:Array = type.split("_");
			switch (typeSplit[typeSplit.length-1])
			{
				case "dodecagon": npoints=12; break;
				case "decagon": npoints=10; break;
				case "nonagon": case "enneagon": npoints=9; break;
				case "octagon": npoints=8; break;
				case "heptagon": npoints=7; break;
				case "hexagon": npoints=6; break;
				case "pentagon": npoints=5; break;
				case "quadrilateral": case "quadrangle": npoints=4; break;
				case "triangle": npoints=3; break;
			}
			switch (type)
			{
				case "dodecagon": case "decagon": case "nonagon": case "enneagon": case "octagon": case "heptagon": case "hexagon": case "pentagon": case "quadrilateral": case "quadrangle": case "triangle":
					if (points.length != npoints){
						return new Array(false, String(_type +" must contain exactly "+ npoints+ " sides and "+ npoints+ " angles.")); 
					} 
					else 
					{ 
						return new Array(true,"");						
					} 
					break;
				case "concave_dodecagon": case "concave_decagon": case "concave_nonagon": case "concave_enneagon": case "concave_octagon": case "concave_heptagon": case "concave_hexagon": case "concave_pentagon": case "concave_quadrilateral": case "concave_quadrangle":
					if (points.length != npoints){
						return new Array(false, String(_type +" must contain exactly "+ npoints+ " sides and "+ npoints+ " angles.")); 
					} 
					else 
					{ 
						if (!checkConvexity(points, true))
						{
							return new Array(true,"");
						} else
						{
							return new Array(false, "At least one of your internal angles needs to be greater than 180 degrees.");
						}
					} 
					break;
				case "convex_dodecagon": case "convex_decagon": case "convex_nonagon": case "convex_enneagon": case "convex_octagon": case "convex_heptagon": case "convex_hexagon": case "convex_pentagon": case "convex_quadrilateral": case "convex_quadrangle":
					if (points.length != npoints){
						return new Array(false, _type +" must contain exactly ", npoints, " sides and ", npoints, " angles."); 
					} 
					else 
					{ 
						if (checkConvexity(points, false))
						{
							return new Array(true,"");
						} else
						{
							return new Array(false, "At least one of your internal angles is greater than 180 degrees.");
						}
					} 
					break;
				case "trapezoid":
					if (points.length == 4) 
					{
						// check parallel sides
						//if (Math.abs((MathUtilities.calcRotation(points[0], points[1])+180)%180 - (MathUtilities.calcRotation(points[2], points[3])+180)%180) < parallel_threshold_degrees || Math.abs((MathUtilities.calcRotation(points[0], points[3])+180)%180 - (MathUtilities.calcRotation(points[1], points[2])+180)%180) < parallel_threshold_degrees)
						if (MathUtilities.calcAcuteRotationAbs(MathUtilities.calcRotation(points[0], points[1]), MathUtilities.calcRotation(points[2], points[3])) < parallel_threshold_degrees || MathUtilities.calcAcuteRotationAbs(MathUtilities.calcRotation(points[0], points[3]), MathUtilities.calcRotation(points[1], points[2])) < parallel_threshold_degrees)
						{
							return new Array(true,"");	
						} else
						{
							return new Array(false, _type +" must contain at least one pair of parallel sides.");
						}
					} else
					{
						return new Array(false, _type + " must contain exactly 4 sides and 4 angles.");
					}
					break;
				case "right_trapezoid":
					if (points.length == 4) 
					{
						// check parallel sides
						//if (Math.abs((MathUtilities.calcRotation(points[0], points[1])+180)%180 - (MathUtilities.calcRotation(points[2], points[3])+180)%180) < parallel_threshold_degrees || Math.abs((MathUtilities.calcRotation(points[0], points[3])+180)%180 - (MathUtilities.calcRotation(points[1], points[2])+180)%180) < parallel_threshold_degrees)
						if (MathUtilities.calcAcuteRotationAbs(MathUtilities.calcRotation(points[0], points[1]), MathUtilities.calcRotation(points[2], points[3])) < parallel_threshold_degrees || MathUtilities.calcAcuteRotationAbs(MathUtilities.calcRotation(points[0], points[3]), MathUtilities.calcRotation(points[1], points[2])) < parallel_threshold_degrees)
						{
							var rightcount:uint=0;
							for (i=0; i<points.length; i++)
							{
								if (Math.abs(MathUtilities.calcAcuteRotationBetween(points[i], points[(i+2)%points.length], points[(i+1)%points.length])-90) < right_threshold_degrees)
								{
									rightcount++;
								}
							}
							// check right angles
							if (rightcount >= 2)
							{
								return new Array(true,"");	
							} else 
							{
								return new Array(false, _type +" must contain two right angles.");
							}
						} else
						{
							return new Array(false, _type +" must contain at least one pair of parallel sides.");
						}
					} else
					{
						return new Array(false, _type + " must contain exactly 4 sides and 4 angles.");
					}
					break;
				case "isosceles_trapezoid":
					if (points.length == 4) 
					{
						// check parallel sides
						//if (Math.abs((MathUtilities.calcRotation(points[0], points[1])+180)%180 - (MathUtilities.calcRotation(points[2], points[3])+180)%180) < parallel_threshold_degrees || Math.abs((MathUtilities.calcRotation(points[0], points[3])+180)%180 - (MathUtilities.calcRotation(points[1], points[2])+180)%180) < parallel_threshold_degrees)
						if (MathUtilities.calcAcuteRotationAbs(MathUtilities.calcRotation(points[0], points[1]), MathUtilities.calcRotation(points[2], points[3])) < parallel_threshold_degrees || MathUtilities.calcAcuteRotationAbs(MathUtilities.calcRotation(points[0], points[3]), MathUtilities.calcRotation(points[1], points[2])) < parallel_threshold_degrees)
						{
							// two opposite sides must be the same length 
							if (Math.abs(MathUtilities.distanceP(points[0], points[1]) - MathUtilities.distanceP(points[3], points[2])) < length_threshold_pixels || Math.abs(MathUtilities.distanceP(points[0], points[3]) - MathUtilities.distanceP(points[2], points[1])) < length_threshold_pixels)
							{
								return new Array(true,"");	
							} else 
							{
								return new Array(false, _type +" must contain one pair of congruent opposite sides.");
							}
						} else
						{
							return new Array(false, _type +" must contain at least one pair of parallel sides.");
						}
					} else
					{
						return new Array(false, _type + " must contain exactly 4 sides and 4 angles.");
					}
					break;
				case "parallelogram":
					if (points.length == 4) 
					{
						// check parallel sides
						
						if (MathUtilities.calcAcuteRotationAbs(MathUtilities.calcRotation(points[0], points[1]), MathUtilities.calcRotation(points[2], points[3])) < parallel_threshold_degrees && MathUtilities.calcAcuteRotationAbs(MathUtilities.calcRotation(points[0], points[3]), MathUtilities.calcRotation(points[1], points[2])) < parallel_threshold_degrees)
							//if (Math.abs((MathUtilities.calcRotation(points[0], points[1])+180)%180 - (MathUtilities.calcRotation(points[2], points[3])+180)%180) < parallel_threshold_degrees && Math.abs((MathUtilities.calcRotation(points[0], points[3])+180)%180 - (MathUtilities.calcRotation(points[1], points[2])+180)%180) < parallel_threshold_degrees)
						{
							return new Array(true,"");	
						} else
						{
							return new Array(false, _type +" must contain two pairs of parallel sides.");
						}
						
					} else
					{
						return new Array(false, _type + " must contain exactly 4 sides and 4 angles.");
					}
					break;
				case "kite":
					if (points.length == 4) 
					{
						// check equal sides
						if (Math.abs(MathUtilities.distanceP(points[0], points[1]) - MathUtilities.distanceP(points[1], points[2])) < length_threshold_pixels && Math.abs(MathUtilities.distanceP(points[0], points[3]) - MathUtilities.distanceP(points[2], points[3])) < length_threshold_pixels || Math.abs(MathUtilities.distanceP(points[0], points[1]) - MathUtilities.distanceP(points[0], points[3])) < length_threshold_pixels && Math.abs(MathUtilities.distanceP(points[1], points[2]) - MathUtilities.distanceP(points[2], points[3])) < length_threshold_pixels)
						{
							return new Array(true,"");	
						} else
						{
							return new Array(false, _type +" must contain two pairs of congruent adjacent sides.");
						}
					} else
					{
						return new Array(false, _type + " must contain exactly 4 sides and 4 angles.");
					}
					break;
				case "rhombus":
					if (points.length == 4) 
					{
						
						//if (Math.abs((MathUtilities.calcRotation(points[0], points[1])+180)%180 - (MathUtilities.calcRotation(points[2], points[3])+180)%180) < parallel_threshold_degrees && Math.abs((MathUtilities.calcRotation(points[0], points[3])+180)%180 - (MathUtilities.calcRotation(points[1], points[2])+180)%180) < parallel_threshold_degrees)
						if (MathUtilities.calcAcuteRotationAbs(MathUtilities.calcRotation(points[0], points[1]), MathUtilities.calcRotation(points[2], points[3])) < parallel_threshold_degrees && MathUtilities.calcAcuteRotationAbs(MathUtilities.calcRotation(points[0], points[3]), MathUtilities.calcRotation(points[1], points[2])) < parallel_threshold_degrees)
						{
							// check equal sides
							if (Math.abs(MathUtilities.distanceP(points[0], points[1]) - MathUtilities.distanceP(points[1], points[2])) < length_threshold_pixels && Math.abs(MathUtilities.distanceP(points[0], points[3]) - MathUtilities.distanceP(points[2], points[3])) < length_threshold_pixels || Math.abs(MathUtilities.distanceP(points[0], points[1]) - MathUtilities.distanceP(points[0], points[3])) < length_threshold_pixels && Math.abs(MathUtilities.distanceP(points[1], points[2]) - MathUtilities.distanceP(points[2], points[3])) < length_threshold_pixels)
							{
								// check equal sides
								if (Math.abs(MathUtilities.distanceP(points[0], points[1]) - MathUtilities.distanceP(points[1], points[2])) < length_threshold_pixels && Math.abs(MathUtilities.distanceP(points[0], points[3]) - MathUtilities.distanceP(points[2], points[3])) < length_threshold_pixels && Math.abs(MathUtilities.distanceP(points[0], points[1]) - MathUtilities.distanceP(points[0], points[3])) < length_threshold_pixels && Math.abs(MathUtilities.distanceP(points[1], points[2]) - MathUtilities.distanceP(points[2], points[3])) < length_threshold_pixels)
								{
									return new Array(true,"");	
								} else
								{
									return new Array(false, _type +" must contain four congruent sides.");
								}
							} else
							{
								return new Array(false, _type +" must contain two pairs of congruent adjacent sides.");
							}
						} else
						{
							return new Array(false, _type +" must contain two pairs of parallel sides.");
						}
						
					} else
					{
						return new Array(false, _type + " must contain exactly 4 sides and 4 angles.");
					}
					break;
				case "rectangle":
					if (points.length == 4) 
					{
						
						//if (Math.abs((MathUtilities.calcRotation(points[0], points[1])+180)%180 - (MathUtilities.calcRotation(points[2], points[3])+180)%180) < parallel_threshold_degrees && Math.abs((MathUtilities.calcRotation(points[0], points[3])+180)%180 - (MathUtilities.calcRotation(points[1], points[2])+180)%180) < parallel_threshold_degrees)
						if (MathUtilities.calcAcuteRotationAbs(MathUtilities.calcRotation(points[0], points[1]), MathUtilities.calcRotation(points[2], points[3])) < parallel_threshold_degrees && MathUtilities.calcAcuteRotationAbs(MathUtilities.calcRotation(points[0], points[3]), MathUtilities.calcRotation(points[1], points[2])) < parallel_threshold_degrees)
						{
							// check equal opposite sides
							if (Math.abs(MathUtilities.distanceP(points[0], points[1]) - MathUtilities.distanceP(points[3], points[2])) < length_threshold_pixels && Math.abs(MathUtilities.distanceP(points[0], points[3]) - MathUtilities.distanceP(points[2], points[1])) < length_threshold_pixels)
							{
								// check right angles
								if (Math.abs(MathUtilities.calcAcuteRotationBetween(points[3], points[1], points[0])-90) < right_threshold_degrees && Math.abs(MathUtilities.calcAcuteRotationBetween(points[0], points[2], points[1])-90) < right_threshold_degrees && Math.abs(MathUtilities.calcAcuteRotationBetween(points[1], points[3], points[2])-90) < right_threshold_degrees && Math.abs(MathUtilities.calcAcuteRotationBetween(points[2], points[0], points[3])-90) < right_threshold_degrees)
								{
									return new Array(true,"");	
								} else 
								{
									return new Array(false, _type +" must contain four right angles.");
								}
							} else
							{
								return new Array(false, _type +" must contain two pairs of congruent opposite sides.");
							}
						} else
						{
							return new Array(false, _type +" must contain two pairs of parallel sides.");
						}
						
					} else
					{
						return new Array(false, _type + " must contain exactly 4 sides and 4 angles.");
					}
					break;
				case "square":
				case "regular_quadrilateral":
				case "regular_quadrangle":
				case "right_rhombus":
					if (points.length == 4) 
					{
						
						//if (Math.abs((MathUtilities.calcRotation(points[0], points[1])+180)%180 - (MathUtilities.calcRotation(points[2], points[3])+180)%180) < parallel_threshold_degrees && Math.abs((MathUtilities.calcRotation(points[0], points[3])+180)%180 - (MathUtilities.calcRotation(points[1], points[2])+180)%180) < parallel_threshold_degrees)
						if (MathUtilities.calcAcuteRotationAbs(MathUtilities.calcRotation(points[0], points[1]), MathUtilities.calcRotation(points[2], points[3])) < parallel_threshold_degrees && MathUtilities.calcAcuteRotationAbs(MathUtilities.calcRotation(points[0], points[3]), MathUtilities.calcRotation(points[1], points[2])) < parallel_threshold_degrees)
						{
							// check equal sides
							if (Math.abs(MathUtilities.distanceP(points[0], points[1]) - MathUtilities.distanceP(points[1], points[2])) < length_threshold_pixels && Math.abs(MathUtilities.distanceP(points[0], points[3]) - MathUtilities.distanceP(points[2], points[3])) < length_threshold_pixels || Math.abs(MathUtilities.distanceP(points[0], points[1]) - MathUtilities.distanceP(points[0], points[3])) < length_threshold_pixels && Math.abs(MathUtilities.distanceP(points[1], points[2]) - MathUtilities.distanceP(points[2], points[3])) < length_threshold_pixels)
							{
								// check equal sides
								if (Math.abs(MathUtilities.distanceP(points[0], points[1]) - MathUtilities.distanceP(points[1], points[2])) < length_threshold_pixels && Math.abs(MathUtilities.distanceP(points[0], points[3]) - MathUtilities.distanceP(points[2], points[3])) < length_threshold_pixels && Math.abs(MathUtilities.distanceP(points[0], points[1]) - MathUtilities.distanceP(points[0], points[3])) < length_threshold_pixels && Math.abs(MathUtilities.distanceP(points[1], points[2]) - MathUtilities.distanceP(points[2], points[3])) < length_threshold_pixels)
								{
									// check right angles
									if (Math.abs(MathUtilities.calcAcuteRotationBetween(points[3], points[1], points[0])-90) < right_threshold_degrees && Math.abs(MathUtilities.calcAcuteRotationBetween(points[0], points[2], points[1])-90) < right_threshold_degrees && Math.abs(MathUtilities.calcAcuteRotationBetween(points[1], points[3], points[2])-90) < right_threshold_degrees && Math.abs(MathUtilities.calcAcuteRotationBetween(points[2], points[0], points[3])-90) < right_threshold_degrees)
									{
										return new Array(true,"");	
									} else 
									{
										return new Array(false, _type +" must contain four right angles.");
									}
								} else
								{
									return new Array(false, _type +" must contain four congruent sides.");
								}
							} else
							{
								return new Array(false, _type +" must contain two pairs of congruent adjacent sides.");
							}
						} else
						{
							return new Array(false, _type +" must contain two pairs of parallel sides.");
						}
						
					} else
					{
						return new Array(false, _type + " must contain exactly 4 sides and 4 angles.");
					}
					break;				
				case "triangle":
					if (points.length == 3) 
					{
						return new Array(true,"");
					} else
					{
						return new Array(false, _type +" must contain exactly 3 sides and 3 angles.");
					}
					break;
				case "isosceles_triangle":
					if (points.length == 3) 
					{
						// check equal sides
						if (Math.abs(MathUtilities.distanceP(points[0], points[1]) - MathUtilities.distanceP(points[1], points[2])) < length_threshold_pixels || Math.abs(MathUtilities.distanceP(points[1], points[2]) - MathUtilities.distanceP(points[2], points[0])) < length_threshold_pixels || Math.abs(MathUtilities.distanceP(points[2], points[0]) - MathUtilities.distanceP(points[0], points[1])) < length_threshold_pixels)
						{
							return new Array(true,"");	
						} else 
						{
							return new Array(false, _type +" must contain a pair of congruent sides.");
						}						
					} else
					{
						return new Array(false, _type +" must contain exactly 3 sides and 3 angles.");
					}
					break;	
				case "equilateral_triangle":
				case "regular_triangle":
					if (points.length == 3) 
					{
						// check equal sides
					if (Math.abs(MathUtilities.distanceP(points[0], points[1]) - MathUtilities.distanceP(points[1], points[2])) < length_threshold_pixels && Math.abs(MathUtilities.distanceP(points[1], points[2]) - MathUtilities.distanceP(points[2], points[0])) < length_threshold_pixels && Math.abs(MathUtilities.distanceP(points[2], points[0]) - MathUtilities.distanceP(points[0], points[1])) < length_threshold_pixels)
						{
							return new Array(true,"");	
						} else 
						{
							return new Array(false, _type +" must contain 3 congruent sides.");
						}						
					} else
					{
						return new Array(false, _type +" must contain exactly 3 sides and 3 angles.");
					}
					break;
				case "scalene_triangle":
					if (points.length == 3) 
					{
						// check for non-equal sides
						if (Math.abs(MathUtilities.distanceP(points[0], points[1]) - MathUtilities.distanceP(points[1], points[2])) > length_threshold_pixels && Math.abs(MathUtilities.distanceP(points[1], points[2]) - MathUtilities.distanceP(points[2], points[0])) > length_threshold_pixels  && Math.abs(MathUtilities.distanceP(points[2], points[0]) - MathUtilities.distanceP(points[0], points[1])) > length_threshold_pixels)
						{
							return new Array(true,"");	
						} else 
						{
							return new Array(false, _type +" must contain 3 incongruent sides.");
						}						
					} else
					{
						return new Array(false, _type +" must contain exactly 3 sides and 3 angles.");
					}
					break;
					break;
				case "right_triangle":
					if (points.length == 3) 
					{
						// check for one right angle 
						if (Math.abs(MathUtilities.calcAcuteRotationBetween(points[0], points[2], points[1]) - 90) < right_threshold_degrees || Math.abs(MathUtilities.calcAcuteRotationBetween(points[1], points[0], points[2]) - 90) < right_threshold_degrees || Math.abs(MathUtilities.calcAcuteRotationBetween(points[2], points[1], points[0]) - 90) < right_threshold_degrees)
						{
							return new Array(true,"");	
						} else 
						{
							return new Array(false, _type +" must contain at least one right angle.");
						}						
					} else
					{
						return new Array(false, _type +" must contain exactly 3 sides and 3 angles.");
					}
					break;	
				case "right_isosceles_triangle":
				case "isosceles_right_triangle":	
					if (points.length == 3) 
					{
						// check for one right angle 
						if (Math.abs(MathUtilities.calcAcuteRotationBetween(points[0], points[2], points[1]) - 90) < right_threshold_degrees || Math.abs(MathUtilities.calcAcuteRotationBetween(points[1], points[0], points[2]) - 90) < right_threshold_degrees || Math.abs(MathUtilities.calcAcuteRotationBetween(points[2], points[1], points[0]) - 90) < right_threshold_degrees)
						{
							// check equal sides
							if (Math.abs(MathUtilities.distanceP(points[0], points[1]) - MathUtilities.distanceP(points[1], points[2])) < length_threshold_pixels || Math.abs(MathUtilities.distanceP(points[1], points[2]) - MathUtilities.distanceP(points[2], points[0])) < length_threshold_pixels || Math.abs(MathUtilities.distanceP(points[2], points[0]) - MathUtilities.distanceP(points[0], points[1])) < length_threshold_pixels)
							{
								return new Array(true,"");	
							} else 
							{
								return new Array(false, _type +" must contain a pair of congruent sides.");
							}	
						} else 
						{
							return new Array(false, _type +" must contain at least one right angle.");
						}						
					} else
					{
						return new Array(false, _type +" must contain exactly 3 sides and 3 angles.");
					}
					break;
				case "right_scalene_triangle":
				case "scalene_right_triangle":	
					if (points.length == 3) 
					{
						// check for one right angle 
						if (Math.abs(MathUtilities.calcAcuteRotationBetween(points[0], points[2], points[1]) - 90) < right_threshold_degrees || Math.abs(MathUtilities.calcAcuteRotationBetween(points[1], points[0], points[2]) - 90) < right_threshold_degrees || Math.abs(MathUtilities.calcAcuteRotationBetween(points[2], points[1], points[0]) - 90) < right_threshold_degrees)
						{
							// check for non-equal sides
							if (Math.abs(MathUtilities.distanceP(points[0], points[1]) - MathUtilities.distanceP(points[1], points[2])) > length_threshold_pixels && Math.abs(MathUtilities.distanceP(points[1], points[2]) - MathUtilities.distanceP(points[2], points[0])) > length_threshold_pixels  && Math.abs(MathUtilities.distanceP(points[2], points[0]) - MathUtilities.distanceP(points[0], points[1])) > length_threshold_pixels)
							{
								return new Array(true,"");	
							} else 
							{
								return new Array(false, _type +" must contain 3 incongruent sides.");
							}	
						} else 
						{
							return new Array(false, _type +" must contain at least one right angle.");
						}						
					} else
					{
						return new Array(false, _type +" must contain exactly 3 sides and 3 angles.");
					}
					break;
				case "obtuse_triangle":
					if (points.length == 3) 
					{
						// check for one obtuse angle 
						if (MathUtilities.calcAcuteRotationBetween(points[0], points[2], points[1]) > 90 || MathUtilities.calcAcuteRotationBetween(points[1], points[0], points[2]) > 90 || MathUtilities.calcAcuteRotationBetween(points[2], points[1], points[0]) > 90)
						{
							return new Array(true,"");	
						} else 
						{
							return new Array(false, _type +" must contain at least one obtuse angle.");
						}						
					} else
					{
						return new Array(false, _type +" must contain exactly 3 sides and 3 angles.");
					}
					break;	
				case "obtuse_isosceles_triangle":
				case "isosceles_obtuse_triangle":
					if (points.length == 3) 
					{
						// check for one obtuse angle 
						if (MathUtilities.calcAcuteRotationBetween(points[0], points[2], points[1]) > 90 || MathUtilities.calcAcuteRotationBetween(points[1], points[0], points[2]) > 90 || MathUtilities.calcAcuteRotationBetween(points[2], points[1], points[0]) > 90 )
						{
							// check equal sides
							if (Math.abs(MathUtilities.distanceP(points[0], points[1]) - MathUtilities.distanceP(points[1], points[2])) < length_threshold_pixels || Math.abs(MathUtilities.distanceP(points[1], points[2]) - MathUtilities.distanceP(points[2], points[0])) < length_threshold_pixels || Math.abs(MathUtilities.distanceP(points[2], points[0]) - MathUtilities.distanceP(points[0], points[1])) < length_threshold_pixels)
							{
								return new Array(true,"");	
							} else 
							{
								return new Array(false, _type +" must contain a pair of congruent sides.");
							}	
						} else 
						{
							return new Array(false, _type +" must contain at least one obtuse angle.");
						}						
					} else
					{
						return new Array(false, _type +" must contain exactly 3 sides and 3 angles.");
					}
					break;	
				case "obtuse_scalene_triangle":
				case "scalene_obtuse_triangle":
					if (points.length == 3) 
					{
						// check for one obtuse angle 
						if (MathUtilities.calcAcuteRotationBetween(points[0], points[2], points[1]) > 90 || MathUtilities.calcAcuteRotationBetween(points[1], points[0], points[2]) > 90 || MathUtilities.calcAcuteRotationBetween(points[2], points[1], points[0]) > 90 )
						{
							// check for non-equal sides
							if (Math.abs(MathUtilities.distanceP(points[0], points[1]) - MathUtilities.distanceP(points[1], points[2])) > length_threshold_pixels && Math.abs(MathUtilities.distanceP(points[1], points[2]) - MathUtilities.distanceP(points[2], points[0])) > length_threshold_pixels  && Math.abs(MathUtilities.distanceP(points[2], points[0]) - MathUtilities.distanceP(points[0], points[1])) > length_threshold_pixels)
							{
								return new Array(true,"");	
							} else 
							{
								return new Array(false, _type +" must contain 3 incongruent sides.");
							}	
						} else 
						{
							return new Array(false, _type +" must contain at least one obtuse angle.");
						}						
					} else
					{
						return new Array(false, _type +" must contain exactly 3 sides and 3 angles.");
					}
					break;	
				case "acute_triangle":
					if (points.length == 3) 
					{
						// check for all acute angles 
						if (MathUtilities.calcAcuteRotationBetween(points[0], points[2], points[1]) < 90 && MathUtilities.calcAcuteRotationBetween(points[1], points[0], points[2]) < 90 && MathUtilities.calcAcuteRotationBetween(points[2], points[1], points[0]) < 90)
						{
							return new Array(true,"");	
						} else 
						{
							return new Array(false, _type +" must contain 3 acute angles.");
						}						
					} else
					{
						return new Array(false, _type +" must contain exactly 3 sides and 3 angles.");
					}
					break;	
				case "acute_isosceles_triangle":
				case "isosceles_acute_triangle":
					if (points.length == 3) 
					{
						// check for all acute angles 
						if (MathUtilities.calcAcuteRotationBetween(points[0], points[2], points[1]) < 90 && MathUtilities.calcAcuteRotationBetween(points[1], points[0], points[2]) < 90 && MathUtilities.calcAcuteRotationBetween(points[2], points[1], points[0]) < 90)
						{
							// check equal sides
							if (Math.abs(MathUtilities.distanceP(points[0], points[1]) - MathUtilities.distanceP(points[1], points[2])) < length_threshold_pixels || Math.abs(MathUtilities.distanceP(points[1], points[2]) - MathUtilities.distanceP(points[2], points[0])) < length_threshold_pixels || Math.abs(MathUtilities.distanceP(points[2], points[0]) - MathUtilities.distanceP(points[0], points[1])) < length_threshold_pixels)
							{
								return new Array(true,"");	
							} else 
							{
								return new Array(false, _type +" must contain a pair of congruent sides.");
							}	
						} else 
						{
							return new Array(false, _type +" must contain 3 acute angles.");
						}						
					} else
					{
						return new Array(false, _type +" must contain exactly 3 sides and 3 angles.");
					}
					break;
				case "acute_scalene_triangle":
				case "scalene_acute_triangle":
					if (points.length == 3) 
					{
						// check for all acute angles 
						if (MathUtilities.calcAcuteRotationBetween(points[0], points[2], points[1]) < 90 && MathUtilities.calcAcuteRotationBetween(points[1], points[0], points[2]) < 90 && MathUtilities.calcAcuteRotationBetween(points[2], points[1], points[0]) < 90)
						{
							// check for non-equal sides
							if (Math.abs(MathUtilities.distanceP(points[0], points[1]) - MathUtilities.distanceP(points[1], points[2])) > length_threshold_pixels && Math.abs(MathUtilities.distanceP(points[1], points[2]) - MathUtilities.distanceP(points[2], points[0])) > length_threshold_pixels  && Math.abs(MathUtilities.distanceP(points[2], points[0]) - MathUtilities.distanceP(points[0], points[1])) > length_threshold_pixels)
							{
								return new Array(true,"");	
							} else 
							{
								return new Array(false, _type +" must contain 3 incongruent sides.");
							}	
						} else 
						{
							return new Array(false, _type +" must contain 3 acute angles.");
						}						
					} else
					{
						return new Array(false, _type +" must contain exactly 3 sides and 3 angles.");
					}
					break;
			}
			return new Array(false,"");
		}
	}
}